﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows;
using System.Windows.Media;
using DocumentFormat.OpenXml.Drawing;

namespace OpenPowerPoint.Conversion
{
    /// <summary>
    /// Converts OpenXML units into usable stuff
    /// </summary>
    public static class UnitConversion
    {
        /// <summary>
        /// Converts Points to English Metrical Units
        /// </summary>
        /// <param name="dxa">The amount of Points</param>
        /// <returns>The equivalent in emu</returns>
        internal static long PtToEmu(double dxa)
        {
            return (long)(dxa * 12700);
        }

        /// <summary>
        /// Converts English Metrical Units to Points
        /// </summary>
        /// <param name="emu">The amount of Emu</param>
        /// <returns>The equivalent in points</returns>
        internal static double EmuToPt(long emu)
        {
            return emu / 12700f;
        }

        /// <summary>
        /// Converts emu to degree
        /// </summary>
        /// <param name="emu">The amount of emu</param>
        /// <returns>The equivalent in degree</returns>
        internal static double EmuToDegree(long emu)
        {
            return emu / 60000f;
        }

        /// <summary>
        /// Converts degree to emu
        /// </summary>
        /// <param name="degree">The amount of degree</param>
        /// <returns>The equivalent in emu</returns>
        internal static long DegreeToEmu(double degree)
        {
            return (long)(degree * 60000f);
        }

        /// <summary>
        /// Converts dxa into local centimeters
        /// </summary>
        /// <param name="dxa">The number to convert</param>
        /// <returns></returns>
        public static double PtToCm(double dxa)
        {
            return dxa / 28.346456692913385826771653543307d;
        }

        /// <summary>
        /// Converts centimeters into dxa
        /// </summary>
        /// <param name="cm">The number to convert</param>
        /// <returns></returns>
        public static double CmToPt(double cm)
        {
            return cm * 28.346456692913385826771653543307d;
        }

        /// <summary>
        /// Gets the additional data stored in an Color structure
        /// </summary>
        /// <param name="color">The color to read out</param>
        /// <returns></returns>
        internal static byte ColorToEx(Color color)
        {
            int result = 0;
            List<int> digits = new List<int>();

            //Create a comparision color
            Color comp = new Color();
            comp.R = color.R;
            comp.G = color.G;
            comp.B = color.B;
            comp.A = color.A;

            digits.Add(0);
            if (color.ScR > comp.ScR) digits[0] += 1;
            if (color.ScR > comp.ScR + 1f / 16384) digits[0] += 1;
            if (color.ScR < comp.ScR) digits[0] += 3;
            if (color.ScR < comp.ScR - 1f / 16384) digits[0] += 1;

            digits.Add(0);
            if (color.ScG > comp.ScG) digits[1] += 1;
            if (color.ScG > comp.ScG + 1f / 16384) digits[1] += 1;
            if (color.ScG < comp.ScG) digits[1] += 3;
            if (color.ScG < comp.ScG - 1f / 16384) digits[1] += 1;

            digits.Add(0);
            if (color.ScB > comp.ScB) digits[2] += 1;
            if (color.ScB > comp.ScB + 1f / 16384) digits[2] += 1;
            if (color.ScB < comp.ScB) digits[2] += 3;
            if (color.ScB < comp.ScB - 1f / 16384) digits[2] += 1;

            digits.Add(0);
            if (color.ScA > comp.ScA) digits[3] += 1;
            if (color.ScA > comp.ScA + 1f / 16384) digits[3] += 1;
            if (color.ScA < comp.ScA) digits[3] += 3;
            if (color.ScA < comp.ScA - 1f / 16384) digits[3] += 1;

            digits = digits.Reverse<int>().ToList();

            result += digits[0] * 1000;
            result += digits[1] * 100;
            result += digits[2] * 10;
            result += digits[3];

            return Convert.ToByte(CinqToDec(result));
        }

        /// <summary>
        /// Sets the additional data into a color structure
        /// </summary>
        /// <param name="color">The base color to use</param>
        /// <param name="ex">The number to store</param>
        /// <returns></returns>
        /// <remarks>Never execute clamp() on this color, it'll break the data stored!</remarks>
        internal static Color ExToColor(Color color, byte ex)
        {
            //First remove old data
            color.R = color.R;
            color.G = color.G;
            color.B = color.B;
            color.A = color.A;

            List<char> digits = DecToCinq((int)ex).ToString().ToCharArray().ToList();
            digits = digits.Reverse<char>().ToList();
            digits.AddRange(new[] { '0', '0', '0', '0' });

            //Encode each digit
            if (digits[0] == '1')
                color.ScR += 1f / 16384;
            else if (digits[0] == '2')
                color.ScR += 2f / 16384;
            else if (digits[0] == '3')
                color.ScR -= 1f / 16384;
            else if (digits[0] == '4')
                color.ScR -= 2f / 16384;

            if (digits[1] == '1')
                color.ScG += 1f / 16384;
            else if (digits[1] == '2')
                color.ScG += 2f / 16384;
            else if (digits[1] == '3')
                color.ScG -= 1f / 16384;
            else if (digits[1] == '4')
                color.ScG -= 2f / 16384;

            if (digits[2] == '1')
                color.ScB += 1f / 16384;
            else if (digits[2] == '2')
                color.ScB += 2f / 16384;
            else if (digits[2] == '3')
                color.ScB -= 1f / 16384;
            else if (digits[2] == '4')
                color.ScB -= 2f / 16384;


            if (digits[3] == '1')
                color.ScA += 1f / 16384;
            else if (digits[3] == '2')
                color.ScA += 2f / 16384;
            else if (digits[3] == '3')
                color.ScA -= 1f / 16384;
            else if (digits[3] == '4')
                color.ScA -= 2f / 16384;


            return color;
        }

        /// <summary>
        /// Gets additional data stored in the font
        /// </summary>
        /// <param name="font">The font to analyze</param>
        /// <returns></returns>
        internal static string FontToEx(FontFamily font)
        {
            int c = font.Source.Split('\0').Count();

            if (c == 1)
                return "";

            return font.Source.Split('\0')[c - 1];
        }

        /// <summary>
        /// Stores additional data in the font
        /// </summary>
        /// <param name="font">The font to write to</param>
        /// <param name="ex">The data string to write</param>
        /// <returns></returns>
        internal static FontFamily ExToFont(FontFamily font, string ex)
        {
            return new FontFamily(font.Source.Split('\0')[0] + "\0" + ex);
        }

        /// <summary>
        /// Converts decimal numbers into cinqual ones
        /// </summary>
        /// <param name="dec">The base10 number (decimal)</param>
        /// <returns>The base5 number</returns>
        internal static int DecToCinq(int dec)
        {
            List<int> numbers = new List<int>();

            while (true)
            {
                numbers.Add(dec % 5);

                dec = dec / 5;

                if (dec == 0) break;
            }

            int ret = 0;

            for (int i = numbers.Count - 1; i >= 0; i--)
            {
                ret *= 10;
                ret += numbers[i];
            }

            return ret;
        }

        /// <summary>
        /// Converts cinqual numbers into decimal ones
        /// </summary>
        /// <param name="cinq">The base5 number</param>
        /// <returns>The base10 (decimal) result</returns>
        internal static int CinqToDec(int cinq)
        {
            List<char> chars = cinq.ToString().ToCharArray().ToList();

            int ret = 0;

            for (int i = 0; i < chars.Count; i++)
            {
                ret *= 5;
                ret += Convert.ToInt32(chars[i].ToString());
            }

            return ret;
        }
    }

    internal static class DrawingConverters
    {
        public static TextDecorationCollection UnderlineToWpf(TextUnderlineValues underline)
        {
            switch (underline)
            {
                case TextUnderlineValues.Single:
                    return TextDecorations.Underline;
                    break;
            }

            return new TextDecorationCollection();
        }
    }
}
